package com.gitee.wsl.android.ext.format

import android.util.Base64
import com.gitee.wsl.jvm.ext.base.UTF8
import com.gitee.wsl.ext.string.isSpaceString
import java.io.UnsupportedEncodingException
import java.nio.charset.Charset
import java.util.Locale
import kotlin.experimental.and

object EncodeExt{
    val HEX_DIGITS_UPPER = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
    val HEX_DIGITS_LOWER = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')

    /**
     * Return Base64-encode bytes.
     *
     * @param input The input.
     * @return Base64-encode bytes
     */
    fun base64Encode(input: String): ByteArray {
        return base64Encode(input.toByteArray())
    }

    /**
     * Return Base64-encode bytes.
     *
     * @param input The input.
     * @return Base64-encode bytes
     */
    fun base64Encode(input: ByteArray): ByteArray {
        return if (input.isEmpty()) ByteArray(0) else Base64.encode(input, Base64.NO_WRAP)
    }

    /**
     * Return Base64-encode string.
     *
     * @param input The input.
     * @return Base64-encode string
     */
    fun base64Encode2String(input: ByteArray): String {
        return if (input.isEmpty()) "" else Base64.encodeToString(input, Base64.NO_WRAP)
    }

    /**
     * Return the bytes of decode Base64-encode string.
     *
     * @param input The input.
     * @return the string of decode Base64-encode string
     */
    fun base64Decode(input: String): ByteArray {
        return if (input.isEmpty()) ByteArray(0) else Base64.decode(input, Base64.NO_WRAP)
    }

    /**
     * Return the bytes of decode Base64-encode bytes.
     *
     * @param input The input.
     * @return the bytes of decode Base64-encode bytes
     */
    fun base64Decode(input: ByteArray): ByteArray {
        return if (input.isEmpty()) ByteArray(0) else Base64.decode(input, Base64.NO_WRAP)
    }

    /**
     * Int to hex string.
     *
     * @param num The int number.
     * @return the hex string
     */
    fun int2HexString(num: Int): String {
        return Integer.toHexString(num)
    }

    /**
     * Hex string to int.
     *
     * @param hexString The hex string.
     * @return the int
     */
    fun hexString2Int(hexString: String): Int {
        return hexString.toInt(16)
    }

    /**
     * Bytes to bits.
     *
     * @param bytes The bytes.
     * @return bits
     */
    fun bytes2Bits(bytes: ByteArray): String {
        if (bytes.isEmpty()) return ""
        val sb = java.lang.StringBuilder()
        for (aByte in bytes) {
            for (j in 7 downTo 0) {
                sb.append(if (aByte.toInt() shr j and 0x01 == 0) '0' else '1')
            }
        }
        return sb.toString()
    }

    /**
     * Bits to bytes.
     *
     * @param bits1 The bits.
     * @return bytes
     */
    fun bits2Bytes(bits1: String): ByteArray {
        var bits = bits1
        val lenMod = bits.length % 8
        var byteLen = bits.length / 8
        // add "0" until length to 8 times
        if (lenMod != 0) {
            for (i in lenMod..7) {
                bits = "0$bits"
            }
            byteLen++
        }
        val bytes = ByteArray(byteLen)
        for (i in 0 until byteLen) {
            for (j in 0..7) {
                bytes[i] = (bytes[i].toInt() shl 1).toByte()
                bytes[i] = (bytes[i].toInt() or bits[i * 8 + j] - '0').toByte()
            }
        }
        return bytes
    }

    /**
     * Bytes to chars.
     *
     * @param bytes The bytes.
     * @return chars
     */
    fun bytes2Chars(bytes: ByteArray): CharArray? {
        val len = bytes.size
        if (len <= 0) return null
        val chars = CharArray(len)
        for (i in 0 until len) {
            chars[i] = (bytes[i].toInt() and 0xff).toChar()
        }
        return chars
    }

    /**
     * Chars to bytes.
     *
     * @param chars The chars.
     * @return bytes
     */
    fun chars2Bytes(chars: CharArray): ByteArray? {
        if (chars.isEmpty()) return null
        val len = chars.size
        val bytes = ByteArray(len)
        for (i in 0 until len) {
            bytes[i] = chars[i].code.toByte()
        }
        return bytes
    }

    /**
     * Bytes to hex string.
     *
     * e.g. bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns "00A8"
     *
     * @param bytes The bytes.
     * @return hex string
     */
    fun bytes2HexString(bytes: ByteArray): String {
        return bytes2HexString(bytes, true)
    }

    /**
     * Bytes to hex string.
     *
     * e.g. bytes2HexString(new byte[] { 0, (byte) 0xa8 }, true) returns "00A8"
     *
     * @param bytes       The bytes.
     * @param isUpperCase True to use upper case, false otherwise.
     * @return hex string
     */
    fun bytes2HexString(bytes: ByteArray, isUpperCase: Boolean): String {
        val hexDigits = if (isUpperCase) HEX_DIGITS_UPPER else HEX_DIGITS_LOWER
        val len = bytes.size
        if (len <= 0) return ""
        val ret = CharArray(len shl 1)
        var i = 0
        var j = 0
        while (i < len) {
            ret[j++] = hexDigits[bytes[i].toInt() shr 4 and 0x0f]
            ret[j++] = hexDigits[(bytes[i] and 0x0f).toInt()]
            i++
        }
        return String(ret)
    }

    /**
     * Hex string to bytes.
     *
     * e.g. hexString2Bytes("00A8") returns { 0, (byte) 0xA8 }
     *
     * @param hexString1 The hex string.
     * @return the bytes
     */
    fun hexString2Bytes(hexString1: String): ByteArray {
        var hexString = hexString1
        if (hexString.isSpaceString) return ByteArray(0)
        var len = hexString.length
        if (len % 2 != 0) {
            hexString = "0$hexString"
            len += 1
        }
        val hexBytes = hexString.uppercase(Locale.getDefault()).toCharArray()
        val ret = ByteArray(len shr 1)
        var i = 0
        while (i < len) {
            ret[i shr 1] = (hex2Dec(hexBytes[i]) shl 4 or hex2Dec(hexBytes[i + 1])).toByte()
            i += 2
        }
        return ret
    }

    private fun hex2Dec(hexChar: Char): Int {
        return when (hexChar) {
            in '0'..'9' -> {
                hexChar - '0'
            }
            in 'A'..'F' -> {
                hexChar - 'A' + 10
            }
            else -> {
                throw IllegalArgumentException()
            }
        }
    }

    /**
     * Bytes to string.
     */
    fun bytes2String(bytes: ByteArray): String {
        return bytes2String(bytes, UTF8.name())
    }

    /**
     * Bytes to string.
     */
    fun bytes2String(bytes: ByteArray, charsetName: String): String {
        return try {
            String(bytes, Charset.forName(charsetName))
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
            String(bytes)
        }
    }

    /**
     * String to bytes.
     */
    fun string2Bytes(string: String): ByteArray {
        return string2Bytes(string, UTF8.name())
    }

    /**
     * String to bytes.
     */
    fun string2Bytes(string: String, charsetName: String): ByteArray {
        return try {
            string.toByteArray(Charset.forName(charsetName))
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
            string.toByteArray()
        }
    }
}


